// ===========================================================================================================================

//(only 'jQuery.Enumerable' will exist because only "jquery.linq.js" is included in the default HTML file (contains linq.js embedded),
// so this is needed to be compatible with "linq.d.ts".  That said, 'this.Enumerable' will exist in the server environment because
// only "linq.js" will be loaded)
//??if (!this.Enumerable && this.jQuery && jQuery.Enumerable)
//    Enumerable = jQuery.Enumerable;

// ===========================================================================================================================

/** This namespace contains the building blocks needed for DreamSpace application development.  Keeping true to MVC patterns,
* the base objects in this namespace are used to build the "view" (UI) and "controller" (core logic) aspects of applications
* separately. To this end, all view related items are in a sub-namespace called "UI".  The goal is that it should be possible
* to never load the UI components, and applications should still run regardless; thus, no required logic should exist in UI
* components.
* Since DreamSpace also allows for platform specific development, any platform specific namespaces (such as
* "Platform.Windows", "Platform.Mac", "Platform.Linux", etc.) can be found under this namespace as well (if the required
* modules are loaded of course).
*/
module DreamSpace.System.Platform {

    // =======================================================================================================================

    export interface PropertyChangedHandler<TSource, TProperty extends { name: any; }> { (source: TSource, property: TProperty): void; }

    export interface INotifyPropertyChanged<TSource, TProperty extends { name: any; }> {
        propertyChanged: EventDispatcher.$Type<TSource, PropertyChangedHandler<TSource, TProperty>>;
    }

    // =======================================================================================================================

    /** Called BEFORE a property changes.
    * This is called if there's a need to modify property values as they are set.  This is similar to a filter function, except
    * the resulting value is persisted.
    * Note: The interceptor function MUST return either the new value passed in, or something else.
    */
    export interface InterceptorCallback<TOwner extends GraphObject, TDataType> { (property: Property<TOwner, TDataType>, newValue: TDataType): TDataType; }

    /** Called AFTER a property changes.
    * This is called if there's a need to be notified when properties have changed.
    * The parameter 'initialValue' is true when a graph item is being initialized for the first time. Use this to prevent
    * updating the UI in special cases (such as in DreamSpace.UI.HTML, which initially prevents updating 'innerHTML',
    * which would destroy the child nodes).
    */
    export interface ListenerCallback<TOwner extends GraphObject, TDataType> { (property?: Property<TOwner, TDataType>): void; }

    /** Called when a property is being read from.
    * When properties are read from, any associated filters are executed to modify the underlying value before it gets returned
    * to the caller (such as when formatting data - example: converting 'M' to 'Male', '20131006' to 'October 6th, 2013', or
    * trimming spaces/formatting text, etc.).
    * Note: The filter function MUST return either the value passed in, or a new value.
    */
    export interface FilterCallback<TOwner extends GraphObject, TDataType> { (property: Property<TOwner, TDataType>, value: TDataType): TDataType; }

    /** By default, properties are updated internally.  Client side handlers allow also updating client side objects related to
    * graph items and their properties.
    * See 'ClientPropertyHandlers' for more details, and examples.
    */
    export interface ClientPropertyHandler<TOwner extends GraphObject, TDataType> {
        /**
        * @param {Property<Object, any>} property The property that is being updated.
        * @param {TDataType} value The value that the property is being updated with.  The actual value set will be 
        */
        (property: Property<TOwner, TDataType>, value: TDataType): TDataType;
    }

    /** Contains handlers used with static property registration to help classify and bridge updates from internal to external
    * client side related objects.  These handlers are ignored server side. */
    export module ClientPropertyHandlers {

        /** Represents a client side property that is used internally only (i.e. does not update a client object property).
        * You can simply use 'null' instead of this as well. */
        export var None: ClientPropertyHandler<GraphItem.$Type, any> = null;

        /** Represents a property that has a direct association with a style object property of the same name for the underlying
        * DOM element object. */
        export function Style<TDataType>(property: Property<Object, TDataType>, value: TDataType): TDataType {
            return (<GraphItem><any>property.owner).__asHTMLElement.__uiElement.style[property.staticProperty.name] = value;
        }

        /** Represents a property that has a direct association with an element's class attribute for the underlying
        * DOM element object. This handler replaces the whole class attribute with the given string value. */
        export function Class(property: Property<Object, string>, value: string): string {
            return (<UI.HTMLElementBase.$Type><any>property.owner).__asHTMLElement.__uiElement.className = value;
        }

        /** Represents a property that has a direct association with a class list item of the same name for the underlying
        * DOM element object. This handler adds the property name (as a class name) when true, and removes it when false. */
        export function ClassName(property: Property<Object, boolean>, value: boolean): boolean {
            var element = (<UI.HTMLElementBase.$Type><any>property.owner).__asHTMLElement.__uiElement;
            if (value) {
                if (!element.classList.contains(property.staticProperty.name))
                    element.classList.add(property.staticProperty.name);
            } else {
                if (element.classList.contains(property.staticProperty.name))
                    element.classList.remove(property.staticProperty.name);
            }
            return value;
        }

        /** Represents a property that has a direct association with the property of the underlying DOM element object. */
        export function Element<TDataType>(property: Property<Object, string>, value: TDataType): TDataType {
            return (<UI.HTMLElementBase.$Type><any>property.owner).__uiElement[property.staticProperty.name] = value;
        }
    }

    /** Represents a collection of properties. */
    export interface IProperties<TDataType> { [index: string]: Property<Object, TDataType>; }

    export class PropertyCollection<TDataType> extends Net.NetworkObject implements INotifyPropertyChanged<GraphObject, Property<Object, TDataType>>, IDisposable {
        // -------------------------------------------------------------------------------------------------------------------

        /** Holds a collection of properties, indexed by property name.
        * Proper usage: Don't remove or add properties from this object, but instead use the 'PropertyCollection' functions.
        * Note: The '__properties' property is undefined until the first property is set.
        */
        __properties: IProperties<TDataType>; // (warning: this may already be set from parsing an HTML template)

        /** A list of callbacks to execute BEFORE a value changes. */
        interceptors: InterceptorCallback<GraphObject, TDataType>[] = null;

        /** A list of callbacks to execute AFTER a value changes. */
        listeners: ListenerCallback<GraphObject, TDataType>[] = null;

        /** A series of callbacks to pass a value through when a property is read.  This allows derived types to convert 
        * or translate inherited property values into new values as they are passed down the inheritance hierarchy chain. */
        filters: FilterCallback<GraphObject, TDataType>[] = null;

        /** Holds a list of registered handlers to be notified when any property changes within this collection item. */
        __propertyChangedHandlers: PropertyChangedHandler<GraphItem.$Type, Property<Object, any>>[] = null;

        // -------------------------------------------------------------------------------------------------------------------

        /** The event object that dispatches events when a property in this collection changes.
        * Note: This is undefined until an event handler is added using the 'PropertyCollection.register...()' functions.
        */
        propertyChanged: EventDispatcher.$Type<GraphObject, PropertyChangedHandler<GraphObject, Property<Object, TDataType>>>;

        // -------------------------------------------------------------------------------------------------------------------

        constructor() {
            super();
        }

        dispose(): void {
            dispose(this.propertyChanged);
            super.dispose();
        }

        // -------------------------------------------------------------------------------------------------------------------

        /**
        * @param {Property} property The source of this event.
        */
        __DoOnPropertyChanging(owner: Object, property: Property<Object, TDataType>, newValue: TDataType): TDataType {
            if (this.interceptors != null)
                for (var i = 0, n = this.interceptors.length; i < n; ++i)
                    newValue = this.interceptors[i].call(owner, property, newValue);
            return newValue;
        }

        /**
        * @param {Property} property The source of this event.
        */
        __DoOnPropertyChanged(owner: Object, property: Property<Object, TDataType>): void {
            if (this.listeners != null)
                for (var i = 0, n = this.listeners.length; i < n; ++i)
                    this.listeners[i].call(owner, property);
        }

        /**
        * @param {Property} property The source of this event.
        * @param {any} value The result of each filter call is passed into this parameter for each successive call (in filter creation order).
        */
        __FilerValue(owner: Object, property: Property<Object, TDataType>, value: TDataType): TDataType {
            if (this.filters != null)
                for (var i = 0, n = this.filters.length; i < n; ++i)
                    value = this.filters[i].call(owner, property, value);
            return value;
        }

        // -------------------------------------------------------------------------------------------------------------------

        /** A list of callbacks to execute BEFORE this value changes. */
        registerInterceptor(interceptor: InterceptorCallback<GraphItem.$Type, TDataType>): void {
            if (!this.interceptors) this.interceptors = [];
            for (var i = this.interceptors.length - 1; i >= 0; --i)
                if (this.interceptors[i] == interceptor) return;
            this.interceptors.push(interceptor);
        }

        unregisterInterceptor(interceptor: InterceptorCallback<GraphItem.$Type, TDataType>): void {
            if (!this.interceptors) return;
            for (var i = this.interceptors.length - 1; i >= 0; --i)
                if (this.interceptors[i] == interceptor) {
                    this.interceptors.splice(i, 1);
                    break;
                }
        }

        // -------------------------------------------------------------------------------------------------------------------

        /** A list of callbacks to execute AFTER this value changes. */
        registerListener(listener: ListenerCallback<GraphItem.$Type, TDataType>): void {
            if (!this.listeners) this.listeners = [];
            for (var i = this.listeners.length - 1; i >= 0; --i)
                if (this.listeners[i] == listener) return;
            this.listeners.push(listener);
        }

        unregisterListener(listener: ListenerCallback<GraphItem.$Type, TDataType>): void {
            if (!this.listeners) return;
            for (var i = this.listeners.length - 1; i >= 0; --i)
                if (this.listeners[i] == listener) {
                    this.listeners.splice(i, 1);
                    break;
                }
        }

        // -------------------------------------------------------------------------------------------------------------------

        /** Filters are called when a property is being read from.
        * Derived types should create filters if there's a need to notify a stored value before use (such as when formatting data,
        * such as converting 'M' to 'Male', '20131006' to 'October 6th, 2013', or trimming spaces/formatting text, etc.).
        */
        registerFilter(filter: FilterCallback<GraphItem.$Type, TDataType>): void {
            if (!this.filters) this.filters = [];
            for (var i = this.filters.length - 1; i >= 0; --i)
                if (this.filters[i] == filter) return;
            this.filters.push(filter);
        }

        unregisterFilter(filter: FilterCallback<GraphItem.$Type, TDataType>): void {
            if (!this.filters) return;
            for (var i = this.filters.length - 1; i >= 0; --i)
                if (this.filters[i] == filter) {
                    this.filters.splice(i, 1);
                    break;
                }
        }

        // -------------------------------------------------------------------------------------------------------------------

        /** Sets an observable property value.
        * Note: Properties can trigger events to allow the UI to update as required.
        * @see also: {@link getValue}
        * @param {boolean} triggerChangeEvent If true (default), causes a change event to be triggered.  Set this to false to prevent any listeners from being called.
        */
        setValue(property: StaticProperty<any>, value: any, triggerChangeEvents?: boolean): void;
        /** Sets an observable property value.
        * Note: Properties can trigger events to allow the UI to update as required.
        * @see also: {@link getValue}
        * @param {boolean} triggerChangeEvent If true (default), causes a change event to be triggered.  Set this to false to prevent any listeners from being called.
        */
        setValue(name: string, value: any, triggerChangeEvents?: boolean): void;
        setValue(index: any, value: any, triggerChangeEvents: boolean = true): void {
            var name = index.name || index;

            if (typeof value === 'object' && value instanceof Property)
                value = (<Property<Object, any>>value).getValue(); // (value is a property instance, so get its value)

            if (!this.__properties)
                this.__properties = Object();

            var property = this.__properties[name];

            if (property === void 0) {
                // ... need to create an instance property - but first, see if there's a matching static property to associate with it ...
                // (note: the static property is pulled by name to make sure the underlying type contains one [must never cross-associate static properties from other types to be safe])
                var currentSP: __IStaticPropertyInfo<any> = this.constructor['$__staticProperties'];
                var sprop = currentSP ? currentSP.namedIndex[index.name || index] : null;
                this.__properties[name] = property = new Property(this, sprop, value); // (instance property doesn't exist yet, so create it now with the given value)
            } else
                property.setValue(value, triggerChangeEvents);
        }

        /** Gets an observable property value from the graph item object using a static property reference.  If no instance 
        * property exists, the default value of any matching static property is returned (which may be undefined).
        * @see also: {@link setValue}
        * @param {any} defaultValue A default value to use if the specified property doesn't exist.
        */
        getValue<TValueType>(property: StaticProperty<TValueType>, defaultValue?: TValueType): TValueType;
        /** Gets an observable property value from the graph item object by property name.  If no instance property exists,
        * the default value of any matching static property is returned (which may be undefined).
        * @see also: {@link setValue}
        * @param {any} defaultValue A default value to use if the specified property doesn't exist.
        */
        getValue<TValueType>(name: string, defaultValue?: TValueType): TValueType;
        getValue<TValueType>(index: any, defaultValue?: TValueType): TValueType {
            if (!this.__properties) return defaultValue;
            var property = this.__properties[index.name || index];
            if (property !== void 0) return property.getValue();
            // ... attempt to get a default value from any matching static type ...
            var currentSP: __IStaticPropertyInfo<any> = this.constructor['$__staticProperties'];
            if (!currentSP) return defaultValue;
            var sprop = currentSP.namedIndex[index.name || index];
            return sprop ? sprop.defaultValue : defaultValue;
        }

        /** Returns the instance property details for the specified static property definition. */
        getProperty<TDataType>(property: StaticProperty<TDataType>, defaultValue?: TDataType): Property<Object, TDataType>;
        /** Returns the instance property details for the specified property name. */
        getProperty<TDataType>(name: string, defaultValue?: TDataType): Property<Object, TDataType>;
        getProperty<TDataType>(index: any): Property<Object, TDataType> {
            if (!this.__properties) return void 0;
            var name = index.name || index;
            return <Property<Object, any>>this.__properties[name];
        }

        // -------------------------------------------------------------------------------------------------------------------

        /** Copies over properties from the specified graph item. */
        copyProperties(obj: GraphObject, ...excludeList: string[]): void {
            for (var pname in obj.__properties)
                if (excludeList && !~excludeList.indexOf(pname))
                    this.setValue(pname, obj.__properties[pname]);
        }

        // -------------------------------------------------------------------------------------------------------------------

        __DoOnAnyPropertyChanged(property: Property<Object, TDataType>) {
            if (this.__propertyChangedHandlers != null)
                for (var i = 0, n = this.__propertyChangedHandlers.length; i < n; ++i)
                    this.__propertyChangedHandlers[i].call(this, this, property);
        }

        // -------------------------------------------------------------------------------------------------------------------

        /** Adds a handler that will be called when a property is added, modified, or deleted.
        * @see also: {@link removePropertyChangedHandler}
        */
        addPropertyChangedHandler(handler: PropertyChangedHandler<GraphObject, Property<Object, TDataType>>) {
            if (!this.__propertyChangedHandlers)
                this.__propertyChangedHandlers = [];
            var i = this.__propertyChangedHandlers.indexOf(handler);
            if (i == -1) this.__propertyChangedHandlers.push(handler);
        }

        /** Removes property change handlers.
        * @see also: {@link addPropertyChangedHandler}
        */
        removePropertyChangedHandler(handler: PropertyChangedHandler<GraphObject, Property<Object, TDataType>>) {
            var i;
            if (this.__propertyChangedHandlers) {
                i = this.__propertyChangedHandlers.indexOf(handler);
                if (i > 0) this.__propertyChangedHandlers.splice(i, 1);
            }
        }

        // -------------------------------------------------------------------------------------------------------------------
    }

    // =======================================================================================================================

    export class GraphObject extends PropertyCollection<any> {
        constructor() { super(); }
    }

    // =======================================================================================================================

    /** An array of graph item events. */
    export interface IEvents { [index: string]: EventDispatcher.$Type<GraphItem.$Type, EventHandler>; }

    /** A simple basic reference to GraphItem types (i.e. type objects, not instances). */
    export interface IGraphObjectType<TDerivedType extends GraphObject> { new (parent?: GraphItem.$Type, ...args): TDerivedType; }
    export interface IGraphItemType<TDerivedType extends GraphItem.$Type> { new (parent?: GraphItem.$Type, ...args): TDerivedType; }

    export interface IGraphItemView {
        onRootChanged: typeof GraphItem.$Type.prototype.onRootChanged;
        onAdded: typeof GraphItem.$Type.prototype.onAdded;
        onRemoved: typeof GraphItem.$Type.prototype.onRemoved;
        onUpdateVisual: typeof GraphItem.$Type.prototype.onUpdateVisual;
        onVisualPropertyUpdated: typeof GraphItem.$Type.prototype.onVisualPropertyUpdated;
    }

    // =======================================================================================================================

    interface __IStaticPropertyInfo<TDataType> extends NativeTypes.IArray<StaticProperty<TDataType>> {
        parent: __IStaticPropertyInfo<any>; // References the parent static property list (if any).  This is null on most base type.
        owner: IClassModule<Object>; // The class type that owns this static property list.
        namedIndex: { [index: string]: StaticProperty<TDataType> }; // A named hash table used to quickly lookup a static property by name.
    };

    export declare function GraphItem(parent?: GraphItem.$Type): GraphItem.$Type;
    AppDomain.registerType("GraphItem", [DreamSpace, System, Platform]);
    /** A graph item represents a single node on the application graph. */
    export module GraphItem {
        ///** Holds a reference to every GraphItem object that exists across the whole client or server side environment.
        //* This "master" collection can be used to quickly look up and/or synchronize objects based simple on a numerical
        //* ID (index) number.  For this reason, it also exists to aid in networking objects as well.
        //*/
        //??export var items: Collections.IndexedObjectCollcetion<GraphItem.$Type> = new Collections.IndexedObjectCollcetion<GraphItem.$Type>();

        // -------------------------------------------------------------------------------------------------------------------

        //?? private static $__staticProperties: StaticProperty[] = [];

        export class $Type extends GraphObject implements IDisposable, ISerializable, IGraphItemView {
            // -------------------------------------------------------------------------------------------------------------------

            /** The associated view, if any. */
            __view: UI.View.$Type<UI.IUIObject> = <any>this; // (view will always point to a view or 'this' [which may an extended graph item with a view implemented!] so there's no need to check if a value is set or not)
            view<TView extends UI.View.$Type<UI.IUIObject>>(type?: UI.IViewType<TView>, createIfNotFound = false): TView {
                return <TView>((!createIfNotFound || !!this.__view) ? this.__view : this.__view = type());
            }
            setView<T extends UI.View.$Type<UI.IUIObject>>(view: T): T;
            setView<TView extends UI.View.$Type<UI.IUIObject>>(type?: { (...args): TView; $Type: { new (...args): TView; } }): TView;
            setView(view: any) {
                if (typeof view == 'function') {
                    if (view.$Type && !this.__view)
                        this.__view = view = view();
                } else if (typeof view == 'object')
                    this.__view = view;
                else
                    return this.__view;
                if (view)
                    (<UI.View.$Type<any>>view).__graphItem = this;
                return view;
            }

            /** The index of this graph item in the parent's '__children' array. */
            __childIndex: number = -1;

            /** The display name for this graph item, which is useful if developing in an IDE. */
            __displayName: string = ""; //??

            /** The parent graph item to this item. */
            __parent: GraphItem.$Type = null;

            /** The children under this graph item. */
            __children: Collections.ObservableCollection<GraphItem.$Type, GraphItem.$Type>;

            /** A reference to the top most graph item. */
            __rootItem: GraphItem.$Type;

            /** A reference to the top most graph item for this template.  Graph tree roots from templates have this set to in
            * order to mark template boundaries.  This is necessary mainly in order to prevent graph item ID collision on
            * templates that are replicated many items (such as list item templates).
            * TIP: Call "mergeTemplate()" on any graph item to merge template trees into the parent tree (which could
            * also be another template).  You can keep calling the function until all nested templates are merged.
            */
            __templateRoot: boolean = false;

            /**
            * Holds a copy of the 'properties' collection before the application starts (set during the initial layout phase).
            * This allows quick restarting the application later if desired.
            */
            __saveStates: { [name: string]: IProperties } = null;

            /** Holds a list of event handlers indexed by event name for this GraphItem instance. */
            __events: IEvents = Object();

            /** False when the UI needs to be updated, and true once the update is completed.
            * This is set to false when a visual property updates, which invalidates the visuals for the associated graph item.
            * For more info, see 'invalidateVisuals()'.
            */
            visualIsValid: boolean = false;
            ///** This event is triggered when one or more visual-based properties get updated, which causes the visuals to invalidate.
            //* This event is only valid on the client side, so handlers can assumed they are in the client environment.
            //*/
            //??visualsInvalidated: EventDispatcher.$Type<GraphItem.$Type, () => void> = new EventDispatcher.$Type<GraphItem.$Type, () => void>(this, "visualsInvalidated");
            private __visualUpdateTimerHandle: number;
            private __invalidatedGraphItems: { [index: number]: GraphItem.$Type; }; // (on the root object, this is the list of graph items with updated visual properties)
            private __updatedVisualProperties: { [name: string]: Property<Object, any>; }; // (these are specific to each individual GraphItem instance [to the root])

            // -------------------------------------------------------------------------------------------------------------------

            isInitialized(): boolean { return !!this.__saveStates; }

            // -------------------------------------------------------------------------------------------------------------------

            /** See 'GraphItem.new()' instead. */
            constructor(parent: GraphItem.$Type = null) {
                super();

                this.__rootItem = this; // (root item must always be set to a valid GraphItem instance, which always defaults to 'self')

                // ... need to initialize the properties to the static defaults ...
                // (note: this pass only creates the property instances in their initial states - no state information is applied)

                var i: number, n: number, p: StaticProperty<any>, type = (<any>this).constructor, sp = type.$__staticProperties;

                if (sp !== void 0)
                    while (sp) {
                        for (i = sp.length - 1; i >= 0; --i) {
                            p = sp[i];
                            this.__properties[p.name] = p.createPropertyInstance(this);
                        }
                        sp = sp.parent; // (each type has its own array of static properties that have a parent reference to the super class)
                    }

                if (parent)
                    parent.addChild(this);
            }

            /** Removes the graph item from it's parent, and releases all unique IDs associated with this item and all child items.
            * When calling 'dispose()', it recursively calls it on all children, so items are disposed from the inner most children,
            * outwards (but only the root disposed object is removed from it's parent).
            */
            dispose(): void {
                //??if (this.__children)
                //    for (var i = 0, n = this.__children.length; i < n; ++i)
                //        (<{ dispose(root: GraphItem.$Type): void; }><any>this.__children[i]).dispose(this); // (force adding a parameter to flag this as the root [and all others as children])

                // ... dispose some object properties (in a certain order) ...
                // (the inner most children should be disposed first)

                dispose([this.__children, this.__events, this.__properties, this.__saveStates], false);

                // ... remove from the parent and reset some properties ...
                // (if no argument exists, then assume this is the root of the disposal)
                if (!arguments.length && this.__parent) // ('__parent' is null if already detached)
                    this.detach();

                super.dispose();
            }

            // -------------------------------------------------------------------------------------------------------------------

            setData(data: SerializedData) {
            }

            getData(data: SerializedData): void {
            }

            // -------------------------------------------------------------------------------------------------------------------

            /** When this is called, a "snapshot" of the graph item property values is made and stored.
            * Use 'loadState()' to reset back to a saved state.
            * @param {string} stateName (optional) A name for this saved state, or leave empty to use the default ISO Date string.
            * Note: The ISO date in this implementation contains only milliseconds to the 1000ths (.001), so saving states in rapid
            * succession may cause overwrites.
            */
            saveState(stateName?: string) {
                if (stateName == void 0)
                    stateName = TimeSpan.now().toISODateString();
                    //??throw Exception.from("The state name '" + stateName + "' is invalid.", this);
                var savedProperties: IProperties;
                if (!this.__saveStates) {
                    this.__saveStates = {};
                    this.__saveStates[stateName] = savedProperties = {};
                } else {
                    savedProperties = this.__saveStates[stateName];
                    if (!savedProperties) this.__saveStates[stateName] = savedProperties = {};
                }
                for (var p in this.__properties)
                    savedProperties[p] = this.__properties[p].clone();
            }

            // -------------------------------------------------------------------------------------------------------------------

            /** Reloads a saved state. */
            loadState(stateName: string, recursive: boolean = true) {
                var i: number, n: number, p: string;
                if (!stateName)
                    throw Exception.from("The state name '" + stateName + "' is invalid.", this);
                var savedProperties: IProperties = this.__saveStates[stateName];
                if (!savedProperties)
                    throw Exception.from("The state '" + stateName + "' does not exist on this graph item.", this);
                this.__properties = {}; // (complete reset)
                for (p in savedProperties)
                    this.__properties[p] = savedProperties[p].clone();
            }

            // -------------------------------------------------------------------------------------------------------------------

            /** Returns the event manager object for the specified event name. */
            getEvent(eventName: string): EventDispatcher.$Type<GraphItem.$Type, EventHandler> {
                return this.__events["__" + eventName + "Event"];
            }

            /** Attaches an event handler to the specified event name. */
            on(eventName: string, handler: EventHandler, eventMode: EventModes = EventModes.Capture): EventDispatcher.$Type<GraphItem.$Type, EventHandler> {
                var existingEvent = this.__events[eventName];
                if (existingEvent === void 0)
                    this.__events[eventName] = existingEvent = EventDispatcher(this, eventName);
                existingEvent.addListener(handler, eventMode);
                if (this.__view && this.__view.on && this.__view.on != UI.View.$Type.prototype.on)
                    this.__view.on(eventName, handler, eventMode);
                return existingEvent;
            }

            /** Detaches an event handler from the specified event name. */
            off(eventName: string, handler: EventHandler) {
                //var existingEvent = this.__events[eventName];
                //if (existingEvent !== void 0)
                //    existingEvent.removeListener(handler);
            }

            /** Removes all event handlers for the specified event name. */
            clearHandlers(eventName: string) {
                var existingEvent = this.__events[eventName];
                if (existingEvent !== void 0)
                    existingEvent.removeAllListeners();
            }

            // -------------------------------------------------------------------------------------------------------------------

            /** This function is called when the root graph item for this object has changed.
            * Override this when you need to implement functionality when the root of a node in the graph tree changes.  For
            * example, this is used to notify UI HTML objects do 
            */
            onRootChanged(oldRoot: GraphItem.$Type, newRoot: GraphItem.$Type): void { /*virtual*/ 
            }

            private __updateRoot(newRoot: GraphItem.$Type) {
                var oldRoot = this.__rootItem;

                if (oldRoot != newRoot) {
                    this.__rootItem = newRoot;

                    if (this.onRootChanged != $Type.prototype.onRootChanged)
                        this.onRootChanged(oldRoot, newRoot);

                    if (this.__children)
                        for (var i = this.__children.length; i >= 0; --i)
                            this.__children[i].__updateRoot(newRoot);

                    if (this.__saveStates == null) {
                        // ... first time initializing: trigger 'changed' events for all properties in case listeners need to update/configure the graph item or underlying element ...
                        for (var pname in this.__properties)
                            this.__properties[pname].reset();

                        this.saveState("default");
                    }
                }
            }

            /** Attaches this graph item to the specified parent object.
            * If the object is connected to a visual element, the element will be added to its parent element also.
            * Note: Override this in derived types that manage visual elements, and make sure to call back to this function.
            */
            attach(parent: GraphItem.$Type, index?: number): GraphItem.$Type { /*virtual*/ 
                if (this.__parent == parent) return this;

                // ... make sure the objects are of the same app domain ...

                if (parent.__appDomain != this.__appDomain)
                    throw Exception.error("{GraphItem}.attach()", "Cannot attach a graph item from one application domain to another.", this);

                // ... make sure the item doesn't exist, and move it from any other parent ...

                if (this.__parent)
                    this.detach();

                if (!parent.__children)
                    parent.__children = new Collections.ObservableCollection<GraphItem.$Type, GraphItem.$Type>();

                this.__parent = parent;

                if (index >= parent.__children.length) {
                    this.__childIndex = parent.__children.length;
                    parent.__children.push(this);
                } else {
                    if (index < 0) {
                        this.__childIndex = 0;
                        parent.__children.unshift(this);
                    } else {
                        this.__childIndex = index;
                        parent.__children.splice(index, 1, this);
                    }
                    // ... if there are other items after the insert point, update the child index properties ...
                    for (++index; index < parent.__children.length; ++index) {
                        parent.__children[index].__childIndex = index;
                    }
                }

                this.__updateRoot(parent.__rootItem); // (copy the current root item to all new child items)

                if (this.onAdded && this.onAdded !== $Type.prototype.onAdded)
                    this.onAdded();
                if (this.__view && this.__view.onAdded)
                    this.__view.onAdded();

                return this;
            }

            addChild(item: GraphItem.$Type): GraphItem.$Type {

                if (!item || !item.attach) return UNDEFINED;

                return item.attach(this);
            }

            /** Insert a graph item at a specific index. */
            insertChild(item: GraphItem.$Type, index: number): GraphItem.$Type {
                return item.attach(this, index);
            }

            /** Detaches this GraphItem from the graph tree (i.e. removes it from the parent object), and optionally disposes it.
            * If the object is connected to a visual element, the element will be removed from its parent also.
            * Note: Override this in derived types that manage visual elements, and make sure to call back to this function.
            */
            detach(dispose: boolean = false): GraphItem.$Type { /*virtual*/ 
                var parent = this.__parent;
                if (parent) { // (prevent any possible cyclical calls)
                    this.__parent = null;
                    if (dispose) DreamSpace.dispose(this); // (make sure children are released first before removing from the parent)
                    parent.removeChildAt(this.__childIndex);
                    this.__childIndex = -1;
                    this.__updateRoot(null);
                    if (dispose) return UNDEFINED;
                } else if (this.dispose == noop) return UNDEFINED; // (already disposed)
                return this;
            }

            /** Removes the item at the specific index from the array of children and returns the item passed in.
            * If no children exist, or the index is invalid, then 'undefined' is returned.
            */
            removeChildAt(itemIndex: number, dispose: boolean = false): GraphItem.$Type {
                if (this.__children && (itemIndex === 0 || itemIndex > 0) && itemIndex < this.__children.length) {
                    var item;

                    if (itemIndex == this.__children.length - 1)
                        this.__children.pop(); // (faster to pop if this is the last item!)
                    else {
                        this.__children.splice(itemIndex, 1)[0];
                        // ... need to shift the indexes after a splice ...
                        while (itemIndex < this.__children.length)
                            this.__children[itemIndex].__childIndex = itemIndex++;
                    }

                    if (item.__parent) item.detach(); // (if this function was called from 'detach()', then '__parent' will be null)
                    if (dispose) item.dispose();

                    if (this.onRemoved != $Type.prototype.onRemoved)
                        this.onRemoved();
                    if (this.__view && this.__view.onAdded)
                        this.__view.onAdded();

                    return item;
                }
                return UNDEFINED;
            }

            /** Removes the specific item from the array of children and returns the item passed in.
            * If 'dispose' is true, then 'dispose()' is called on the item.
            */
            removeChild(item: GraphItem.$Type, dispose: boolean = false): GraphItem.$Type {
                if (item && this.__children)
                    if (item.__childIndex === 0 || item.__childIndex > 0)
                        this.removeChildAt(item.__childIndex, dispose);
                return item;
            }

            /** Removes AND returns all the children.  If no children exist, then null is returned. */
            removeAllChildren(dispose: boolean = false): GraphItem.$Type[] {
                if (!this.__children || this.__children.length == 0) return null;
                var items: GraphItem.$Type[] = this.__children, item: GraphItem.$Type;
                for (var i = items.length - 1; i >= 0; --i)
                    items[i].detach(dispose);
                this.__children = void 0;
                if (dispose) return UNDEFINED;
                return items;
            }

            // -------------------------------------------------------------------------------------------------------------------

            /** Triggered when this graph item is added to a parent item. */
            onAdded(): void { } /*virtual*/

            /** Triggered when this graph item is removed from a parent item. */
            onRemoved(): void { } /*virtual*/

            /** Called when visual-based properties have updated. */
            onUpdateVisual(): void { } /*virtual*/

            /** Called when a visual-based property has updated. */
            onVisualPropertyUpdated(property: Property<Object, any>): void { } /*virtual*/

            ///** If client side, calling this invalidates the visuals, triggering updates where needed.
            //* Setting visual-based properties will invalidate the visuals, which then sets a timer to execute immediately when
            //* the script finish executing.  As well, each visual-based property update resets the timer, so there's little
            //* performance lost when updating a large number of visual properties during code execution. Once the timer is
            //* triggered, it in turn triggers the 'visualUpdateHandlers' event property when the timer finally triggers.
            //* Note: The update timer is set to 0 ms, so it is placed immediately in the queue to get triggered once the
            //* script stops executing.
            //*/
            /** Called when a visual property is updated.
            * Setting visual-based properties will cause the properties to be placed into a visual update queue.  This causes a
            * timer to be set, which executes when the script finishes.  Since the timer is queued to run after the script completes,
            * there's little performance lost when updating a large number of visual properties during code execution. Once the timer
            * is triggered, it in turn triggers the 'visualUpdateHandlers' event property when the timer finally triggers.
            * Note: The update timer is set to 0 ms, so it is placed immediately in the queue to get triggered once the
            * script stops executing.
            */
            private __visualPropertyUpdated(property: Property<Object, any>): void {
                $Type.prototype.__visualPropertyUpdated = isClient() ? // (self context switch based on client/server sides)
                (property: Property<Object, any>): void => {
                    // ... make sure the information required is correct on the property first ...
                    if (!property.owner)
                        throw Exception.from("The specified property does not have a GraphItem owner.", property);
                    else if (!property.staticProperty)
                        throw Exception.from("The specified property does not have a corresponding static property reference.", property);
                    else if (typeof property.owner.__id != 'number')
                        throw Exception.from("The specified property owner's \"__id\" value (" + property.owner.__id + ") is not valid.", property);

                    // ... make a reference to the updated property, if necessary ...

                    if (this.onVisualPropertyUpdated != $Type.prototype.onVisualPropertyUpdated) { // (if not equal, then it's a function on a derived class [the default one does nothing])
                        if (!this.__updatedVisualProperties)
                            this.__updatedVisualProperties = {};
                        this.__updatedVisualProperties[property.staticProperty.name] = property;
                    }

                    this.visualIsValid = false; // (flag that this graph item has invalidated visuals)

                    this.__rootItem.__invalidateGraphItem(this);
                } : noop;
            }

            private __invalidateGraphItem(item: GraphItem.$Type) { // (make sure to do this on the root item only, which will act as the "hub" for visual property updates [sharing a single timer])
                // ... make sure the information required is correct on the property first ...
                if (typeof item.__id != 'number')
                    throw Exception.from("The specified graph item's \"__id\" value (" + item.__id + ") is not valid.", item);

                // ... make sure to call this on the ROOT item ...
                if (this.__rootItem != this)
                    this.__rootItem.__invalidateGraphItem(item);
                else {
                    if (!this.__invalidatedGraphItems)
                        this.__invalidatedGraphItems = {};

                    this.__invalidatedGraphItems[item.__id] = item;

                    // ... if a timer is not yet created, then create one (only need to do this once, which will reset itself after the timer fires) ...
                    if (this.__visualUpdateTimerHandle === void 0)
                        this.__visualUpdateTimerHandle = setTimeout(GraphItem.$Type.__visualUpdateTimerHandler, 0, this); // (WARNING: Scripts are still running, and can change the root)

                    // (note: if this item becomes disconnected from the parent, then the timer should be cancelled, and a new one created on the new root)
                }
            }

            /** Called by a UI update timer, which will pass the graph item that has updated UI properties that need to be handled. */
            private static __visualUpdateTimerHandler(item: GraphItem.$Type): void {
                // ... make sure to always operate on the ROOT item (it may have changed before the timer event fired) ...
                item = item.__rootItem;

                // ... first, reset the handle to allow another timer to be created (which may happen before returning) ...
                item.__visualUpdateTimerHandle = void 0;

                // ... iterate through all the properties, calling handlers if any are set ...
                var itemCounter: number = 0; // (if this is more than 100, the efficiency drops off, so replace the object [http://jsperf.com/new-object-or-set-properties-to-undefined] - note: object replacement speed is similar to iterating over up to 100 undefined items)
                var propCounter: number;

                for (var id in item.__invalidatedGraphItems) {
                    ++itemCounter;
                    var item = item.__invalidatedGraphItems[id];
                    if (!item) continue;
                    if (!item.visualIsValid
                        && item.onVisualPropertyUpdated != $Type.prototype.onVisualPropertyUpdated) { // (make sure it is overridden [the default does nothing])
                        propCounter = 0;
                        for (var pname in item.__updatedVisualProperties) {
                            ++propCounter;
                            var property = item.__updatedVisualProperties[pname];
                            if (!property) continue;
                            item.onVisualPropertyUpdated(property);
                            item.__updatedVisualProperties[pname] = void 0;
                        }
                        if (propCounter > 100)
                            item.__updatedVisualProperties = {}; // (getting too large now, so refresh)
                    }

                    // ... done with properties, now notify the object itself ...
                    if (item.onUpdateVisual != $Type.prototype.onUpdateVisual) // (if not equal, then it's a function on a derived class [the default one does nothing])
                        item.onUpdateVisual();
                    //??if (item.visualsInvalidated.hasHandlers())
                    //    item.visualsInvalidated.dispatch();

                    // ... clear the property reference (may be faster than replacing the whole object each time) ...
                    item.__invalidatedGraphItems[id] = void 0;
                }
                if (itemCounter > 100)
                    item.__invalidatedGraphItems = {}; // (getting too large now, so refresh)
            }

            // -------------------------------------------------------------------------------------------------------------------

            /** Returns the graph item with the specified ID (similar to 'getElementById()'). */
            getItem<T extends GraphItem.$Type>(id: string): T { // TODO: Create a hash list for the IDs instead.
                var prop = this.__properties['id'], i, n, item;
                if (prop && prop.getValue() == id) return <T>this;
                if (this.__children)
                    for (i = 0, n = this.__children.length; i < n; ++i) {
                        item = this.__children[i].getItem(id);
                        if (item !== void 0) return item;
                    }
                return void 0;
            }

            // -------------------------------------------------------------------------------------------------------------------
            // LinqJS Support

            /** Returns an enumerable that can be used to iterate over the child graph items. */
            asEnumerable(): linq.Enumerable {
                return this.__children ? Enumerable.From(this.__children) : Enumerable.Empty();
            }

            // -------------------------------------------------------------------------------------------------------------------



            // -------------------------------------------------------------------------------------------------------------------
        }
        AppDomain.registerClass(GraphItem);
    }

    // =======================================================================================================================

    export interface IDataTemplate {
        id: string;
        originalHTML: string;
        templateHTML: string;
        templateItem: GraphItem.$Type;
    }

    export declare function Application(parent?: GraphItem.$Type, title?: string, description?: string, targetElement?: HTMLElement): Application.$Type;
    AppDomain.registerType("Application", [DreamSpace, System, Platform, Application]);
    /** An application object encapsulates the configuration for a single application project. */
    export module Application {
        export var applications: Application.$Type[] = [];

        export class $Type extends GraphItem.$Type {

            // ---------------------------------------------------------------------------------------------------------------

            // ---------------------------------------------------------------------------------------------------------------

            /** A title for the application (required). */
            title: typeof Application.Title.propertyGetSetHandler;
            _title: typeof Application.Title.defaultValue;

            /** A short application description (optional). */
            description: typeof Application.Description.propertyGetSetHandler;
            _description: typeof Application.Description.propertyGetSetHandler;

            /** The application version number (i.e. '#.#.#...') in string form. */
            version: typeof Application.Version.propertyGetSetHandler;
            _version: typeof Application.Version.propertyGetSetHandler;


            /** This is the target element that the application will render to when updateLayout() is called. */
            private targetElement: HTMLElement;
            getTargetElement(): HTMLElement { return this.targetElement; }

            /** Holds a list of data templates found in the parsed HTML page template.
            * Templates are based on elements within the page which are marked by the 'data--template="someID"' attribute. These
            * elements are stripped out and are not part of the resulting page.
            */
            dataTemplates: { [id: string]: IDataTemplate; } = {};

            // -------------------------------------------------------------------------------------------------------------------

            constructor(parent: GraphItem.$Type = null, title: string = "", description: string = "", targetElement: HTMLElement = null) {
                super(parent);
                if (typeof title == "undefined" || "" + title == "")
                    throw "An application title is required.";

                this.title(title);
                this.description(description);
                this.targetElement = targetElement || document.getElementsByTagName("body")[0] || null;

                applications.push(this);
            }

            dispose(): void {
                applications.splice(applications.indexOf(this), 1);
                super.dispose();
            }

            // -------------------------------------------------------------------------------------------------------------------

            ///** This generates/updates the HTML elements required to display the application. */
            //??updateLayout(recursive: boolean = true) {
            //    var log = debug ? Diagnostics.log(Modules.$_System.Platform, "Application is updating its layout ...").beginCapture() : null;

            //    super.updateLayout(recursive);

            //    if (this.__element != null) {
            //        var node = <HTMLElement>this.targetElement.firstChild;
            //        if (node == null || !node.replaceNode)
            //            this.targetElement.appendChild(this.__element);
            //        else
            //            node.replaceNode(this.__element);
            //    }

            //    if (log)
            //        log.write("Application layout updated.").parent.endCapture();
            //}

            // -------------------------------------------------------------------------------------------------------------------

            /** Imports an HTML template starting at the given DOM element (a basic DOM system is required for traversing the elements).  Any existing graph items
            * will be merged or replaced.
            * If '$DS.isServer()' returns true, then you should call 'parseTemplate()'
            * @param {HTMLElement} element HTML root element to traverse.
            */
            applyTemplate(element: HTMLElement) {
                // ... 'element' is the application root ...
            }

            /** Parses HTML text to build the visual graph tree.
            * This concept is similar to using XAML to load objects in Silverlight. You have the option to use an HTML file, or dynamically build your application
            * directly in code (just like with Silverlight XAML/C#/VB/etc.).
            * 
            * Warning about inline scripts: Script tags will be execute client side (naturally by the DOM), but you cannot rely on them server side.  Try to use
            * HTML for UI DESIGN ONLY.  Expect that any code you place in the HTML will not execute server side.
            */
            parseTemplate(html: string = null) {
                var log = Diagnostics.log(Modules.$_System.Platform, "Parsing application HTML template ...").beginCapture();

                if (!html)
                    if ($DS.isClient())
                        html = this.loadTemplate(); // (returns "<html>...</html>" from the DOM)
                    else
                        throw Exception.from("Unable to parse the client side HTML on the server side (no HTML is loaded).  Call 'loadTemplate()' instead.");

                // ... parse the HTML "body" section for the graph tree ("body" is the application root) ...
                // (quickly scan the template text to create the required objects [using <body> as the application root])

                document.body.innerHTML = ""; // (clear this if not already done so [will be dynamically rebuilt using the graph tree before the user sees any changes])
                // (allowing the innterHTML to exist first provides a quick visual view of the page, which will then give time to quickly replace it)

                var result = Platform.parse(html);

                for (var i = 0, n = result.rootElements.length; i < n; ++i)
                    this.addChild(result.rootElements[i]);

                for (var id in result.templates)
                    this.dataTemplates[id] = result.templates[id];

                log.write("Application HTML template parsing complete.").parent.endCapture();
            }

            /** Loads the 'htmlFile' file and calls 'parseTemplate()' with the contents.
            * The call is synchronous, and only returns when completed.
            * @param {string} htmlFileURI The URI to the HTML template file. If empty or undefined, then the HTML is pulled from the loaded DOM (see 'parseTemplate()').
            * @param {boolean} fallbackToDOM If true (the default), then the HTML template will be extracted from the currently loaded DOM as a fallback (i.e. if the
            * URI is invalid, or access fails due to a sudden connection failure).
            */
            loadTemplate(htmlFileURI?: string, fallbackToDOM: boolean = true): string {
                var contents = null;
                var log = Diagnostics.log("Application.loadTemplate()", "Loading template from '" + htmlFileURI + "' ...").beginCapture();
                // ... load the file ...
                if ($DS.isClient()) {
                    if (htmlFileURI) {
                        // ... use AJAX ...
                        var request = new XMLHttpRequest();
                        request.open("get", htmlFileURI + "!", false);
                        request.onerror = (ev: ErrorEvent) => {
                            Diagnostics.log("Application.loadTemplate()", ev.message, Diagnostics.LogTypes.Error);
                        };
                        request.onreadystatechange = () => {
                            if (request.readyState == (XMLHttpRequest.DONE || 4)) {
                                var ok = false, response = request.response || request.responseText || request.responseXML || request.responseBody;
                                if (request.status == 200) {
                                    log.write("Template data was loaded from the server.", Diagnostics.LogTypes.Success);
                                    ok = true;
                                } else if (request.status == 304) {
                                    log.write("Template data was loaded from the browser cache.", Diagnostics.LogTypes.Success);
                                    ok = true;
                                } else
                                    log.write("Template data was not loaded. Response: " + response, Diagnostics.LogTypes.Error); // (usually at 404)
                                if (ok)
                                    contents = response;
                            }
                        };
                        log.write("Sending request for template ...", Diagnostics.LogTypes.Info);
                        request.send();
                    }
                    if (!contents && fallbackToDOM) {
                        log.write("Failed to load the template content, or content is empty.  Using the existing DOM content instead.", Diagnostics.LogTypes.Warning);
                        // ... get HTML from the DOM as a fall back [there should be no reason not to have something!] ...
                        contents = document.getElementsByTagName("html")[0].outerHTML;
                    }
                } else {
                    // ... use the host bridge ...
                    // contents = loadedContent;
                }
                log.endCapture();
                return contents;
            }

            // -------------------------------------------------------------------------------------------------------------------

            /** Returns a template with the specified ID.  Returns null if nothing was found. */
            getDataTemplate(id: string): IDataTemplate {
                return this.dataTemplates && this.dataTemplates[id] || null;
            }

            // -------------------------------------------------------------------------------------------------------------------

            /** Return the application, and all child graph items, as a single JSON string.  The values are all graph objects and related REGISTERED properties
            * only - direct instance properties are not all persisted, if any.
            * JavaScript 1.7 implements the 'JSON.stringify()' function to convert values into the JSON format.  The implementation looks for a 'toJSON()'
            * function on an object and calls it if found.
            */
            toJSON(): string { // (see http://stackoverflow.com/questions/14991374/how-does-moment-js-know-when-its-object-is-being-serialised/14991571#14991571)
                return null;
            }

            // -------------------------------------------------------------------------------------------------------------------
        }

        export var Title = Property.register($Type, "title", true, "");
        export var Description = Property.register($Type, "description", true, "");
        export var Version = Property.register($Type, "version", true, "");
    }
    AppDomain.registerClass(Application);

    // -------------------------------------------------------------------------------------------------------------------
    
    /** Parses HTML to create a graph object tree, and also returns any templates found.
    * This concept is similar to using XAML to load objects in Silverlight. You have the option to use an HTML template, or dynamically build your
    * graph items directly in code (just like with Silverlight XAML/C#/VB/etc.).
    * 
    * Warning about inline scripts: Script tags may be executed client side (naturally by the DOM), but you cannot rely on them server side.  Try to use
    * HTML for UI DESIGN ONLY.  Expect that any code you place in the HTML will not execute server side (or client side for that matter) unless you
    * handle/execute the script code yourself.
    * @param {string} html The HTML to parse.
    * @param {boolean} strictMode If true, then the parser will produce errors on ill-formed HTML (eg. 'attribute=' with no value).
    * This can greatly help identify possible areas of page errors.  If strict formatting is not important, pass in false.
    */
    export function parse(html: string = null, strictMode?: boolean): { rootElements: GraphItem.$Type[]; templates: { [id: string]: IDataTemplate; } } {
        using(Modules.$_System.UI, true); // (make sure this is loaded first!)

        var log = Diagnostics.log(Modules.$_System.Platform, "Parsing HTML template ...").beginCapture();
        log.write("Template: " + html);

        if (!html) return null;

        // ... parsing is done by passing each new graph item the current scan position in a recursive pattern, until the end of the HTML text is found ...
        var htmlReader = new Markup.HTMLReader(html);
        if (strictMode !== void 0)
            htmlReader.strictMode = !!strictMode;

        var approotID: string;
        var mode: number = 0; // (1 = ready on next tag, 2 = creating objects)
        var classMatch = /^[$.][A-Za-z0-9_$]*(\.[A-Za-z0-9_$]*)*(\s+|$)/;
        var attribName: string;

        var storeRunningText = (parent: GraphItem.$Type) => {
            if (htmlReader.runningText) { // (if there is running text, then create a text node for it for the CURRENT graph item [not the parent])
                if (!UI)
                    GraphItem(parent).setValue((htmlReader.runningText.indexOf('&') < 0 ? "text" : "html"), htmlReader.runningText);
                else if (htmlReader.runningText.indexOf('&') < 0)
                    UI.HTML.PlainText(parent, htmlReader.runningText);
                else
                    UI.HTML.HTMLText(parent, htmlReader.runningText);
            }
        };

        var rootElements: GraphItem.$Type[] = [];
        var dataTemplates: { [id: string]: IDataTemplate; } = {};

        var processTags = (parent: GraphItem.$Type): IDataTemplate[]=> { // (returns the data templates found for the immediate children only)
            var graphItemType: string, graphItemTypePrefix: string;
            var graphType: IClassModule<UI.HTMLElementBase.$Type>;
            var graphItem: Platform.UI.HTMLElementBase.$Type;
            var properties: {};
            var currentTagName: string;
            var isDataTemplate: boolean = false, dataTemplateID: string, dataTemplateHTML: string;
            var tagStartIndex: number, lastTemplateIndex: number;
            var templateInfo: IDataTemplate;
            var templates: IDataTemplate[] = null;
            var immediateChildTemplates: IDataTemplate[] = null;

            while (htmlReader.readMode != Markup.HTMLReaderModes.End) {

                currentTagName = htmlReader.tagName;

                if (!htmlReader.isMarkupDeclaration() && !htmlReader.isCommentBlock() && !htmlReader.isScriptBlock() && !htmlReader.isStyleBlock()) {

                    if (currentTagName == "html") {
                        if (approotID === void 0)
                            approotID = null; // (null flags that an HTML tag was found)

                        if (htmlReader.attributeName == "data-approot" || htmlReader.attributeName == "data--approot") {
                            approotID = htmlReader.attributeValue;
                        }
                    }
                    else {
                        // (note: app root starts at the body by default, unless a root element ID is given in the HTML tag before hand)

                        if (htmlReader.readMode == Markup.HTMLReaderModes.Attribute) {

                            // ... templates are stripped out for usage later ...

                            if (!isDataTemplate && htmlReader.attributeName == "data--template") {
                                isDataTemplate = true; // (will add to the template list instead of the final result)
                                dataTemplateID = htmlReader.attributeValue;
                            }

                            attribName = (htmlReader.attributeName.substring(0, 6) != "data--") ? htmlReader.attributeName : htmlReader.attributeName.substring(6);
                            properties[attribName] = htmlReader.attributeValue;

                            if (htmlReader.attributeName == "id" && htmlReader.attributeValue == approotID && mode == 0)
                                mode = 1;
                        }
                        else {
                            if (mode == 2 && htmlReader.readMode == Markup.HTMLReaderModes.Tag && htmlReader.isClosingTag()) { // (this an ending tag (i.e. "</...>"))
                                // (this end tag should be the "closure" to the recently created graph item sibling, which clears 'graphiItem', but if
                                // it's already null, the end tag should be handled by the parent level; also, if the closing tag name is different
                                // (usually due to ill-formatted HTML [allowed only on parser override], or auto-closing tags, like '<img>'), assume
                                // closure of the previous tag and let the parent handle it)
                                if (graphItem) {
                                    storeRunningText(graphItem);

                                    if (isDataTemplate) {
                                        dataTemplateHTML = htmlReader.getHTML().substring(tagStartIndex, htmlReader.textEndIndex) + ">";
                                        templateInfo = { id: dataTemplateID, originalHTML: dataTemplateHTML, templateHTML: undefined, templateItem: graphItem };
                                        // (note: if there are immediate child templates, remove them from the current template text)
                                        if (immediateChildTemplates)
                                            for (var i = 0, n = immediateChildTemplates.length; i < n; i++)
                                                dataTemplateHTML = dataTemplateHTML.replace(immediateChildTemplates[i].originalHTML, "<!--{{" + immediateChildTemplates[i].id + "Items}}-->");
                                        templateInfo.templateHTML = dataTemplateHTML;
                                        dataTemplates[dataTemplateID] = templateInfo; // (all templates are recorded in application scope, so IDs must be unique, otherwise they will override previous ones)
                                        if (!templates) templates = [];
                                        templates.push(templateInfo);
                                        isDataTemplate = false;
                                    }

                                    if (htmlReader.tagName != graphItem.htmlTag)
                                        return templates; // (note: in ill-formatted html [optional feature of the parser], make sure the closing tag name is correct, else perform an "auto close and return")

                                    graphType = null;
                                    graphItem = null;
                                    immediateChildTemplates = null;
                                }
                                else return templates; // (return if this closing tag doesn't match the last opening tag read, so let the parent level handle it)
                            }
                            else if (mode == 2 && htmlReader.readMode == Markup.HTMLReaderModes.EndOfTag) { // (end of attributes, so create the tag graph item)

                                // ... this is either the end of the tag with inner html/text, or a self ending tag (XML style) ...

                                graphItemType = properties['class']; // (this may hold an explicit object type to create [note expected format: module.full.name.classname])
                                graphItem = null;
                                graphType = null;

                                if (graphItemType && classMatch.test(graphItemType)) {
                                    graphItemTypePrefix = RegExp.lastMatch.substring(0, 1); // ('$' [DS full type name prefix], or '.' [default UI type name])

                                    if (graphItemTypePrefix == '$') {
                                        graphItemType = RegExp.lastMatch.substring(1);
                                        if (graphItemType.charAt(0) == '.') // (just in case there's a '.' after '$', allow this as well)
                                            graphItemTypePrefix = '.';
                                    } else
                                        graphItemType = RegExp.lastMatch; // (type is either a full type, or starts with '.' [relative])

                                    if (graphItemTypePrefix == '.')
                                        graphItemType = "DreamSpace.System.UI" + graphItemType;

                                    graphType = <typeof UI.HTMLElementBase>Utilities.dereferencePropertyPath(DreamSpace.global, Modules.translateModuleTypeName(graphItemType));

                                    if (graphType === void 0)
                                        throw Exception.from("The graph item type '" + graphItemType + "' for tag '<" + currentTagName + "' on line " + htmlReader.getCurrentLineNumber() + " was not found.");

                                    if (typeof graphType !== 'function' || typeof graphType.$Type === void 0)
                                        throw Exception.from("The graph item type '" + graphItemType + "' for tag '<" + currentTagName + "' on line " + htmlReader.getCurrentLineNumber() + " does not resolve to a GraphItem class type.");
                                }

                                if (graphType == null) {
                                    if (UI) { // (make sure the UI module is available)
                                        // ... auto detect the DreamSpace UI GraphItem type based on the tag name (all valid HTML4/5 tags: http://www.w3schools.com/tags/) ...
                                        switch (currentTagName) {
                                            case 'a': graphType = UI.HTML.Anchor; break;

                                            // (phrases)
                                            case 'abbr': graphType = UI.HTML.Phrase; properties['phraseTypes'] = UI.HTML.PhraseTypes.Abbreviation; break;
                                            case 'acronym': graphType = UI.HTML.Phrase; properties['phraseTypes'] = UI.HTML.PhraseTypes.Acronym; break;
                                            case 'em': graphType = UI.HTML.Phrase; properties['phraseTypes'] = UI.HTML.PhraseTypes.Emphasis; break;
                                            case 'strong': graphType = UI.HTML.Phrase; properties['phraseTypes'] = UI.HTML.PhraseTypes.Strong; break;
                                            case 'cite': graphType = UI.HTML.Phrase; properties['phraseTypes'] = UI.HTML.PhraseTypes.Cite; break;
                                            case 'dfn': graphType = UI.HTML.Phrase; properties['phraseTypes'] = UI.HTML.PhraseTypes.Defining; break;
                                            case 'code': graphType = UI.HTML.Phrase; properties['phraseTypes'] = UI.HTML.PhraseTypes.Code; break;
                                            case 'samp': graphType = UI.HTML.Phrase; properties['phraseTypes'] = UI.HTML.PhraseTypes.Sample; break;
                                            case 'kbd': graphType = UI.HTML.Phrase; properties['phraseTypes'] = UI.HTML.PhraseTypes.Keyboard; break;
                                            case 'var': graphType = UI.HTML.Phrase; properties['phraseTypes'] = UI.HTML.PhraseTypes.Variable; break;

                                            // (headers)
                                            case 'h1': graphType = UI.HTML.Header; properties['headerLevel'] = 1; break;
                                            case 'h2': graphType = UI.HTML.Header; properties['headerLevel'] = 2; break;
                                            case 'h3': graphType = UI.HTML.Header; properties['headerLevel'] = 3; break;
                                            case 'h4': graphType = UI.HTML.Header; properties['headerLevel'] = 4; break;
                                            case 'h5': graphType = UI.HTML.Header; properties['headerLevel'] = 5; break;
                                            case 'h6': graphType = UI.HTML.Header; properties['headerLevel'] = 6; break;

                                            default: graphType = UI.HTML.__HTMLElement; // (just create a basic object to use with htmlReader.tagName)
                                        }
                                    } else graphType = UI.HTML.__HTMLElement; // (UI not loaded, so just create simple GraphItem objects)
                                }

                                if (!graphItem) { // (only create if not explicitly created)
                                    graphItem = graphType(isDataTemplate ? null : parent);
                                }

                                for (var pname in properties)
                                    graphItem.setValue(pname, properties[pname], true);

                                graphItem.htmlTag = currentTagName;

                                // ... some tags are not allowed to have children (and don't have to have closing tags, so null the graph item and type now)...
                                switch (currentTagName) {
                                    case "area": case "base": case "br": case "col": case "command": case "embed": case "hr": case "img": case "input":
                                    case "keygen": case "link": case "meta": case "param": case "source": case "track": case "wbr":
                                        graphItem = null;
                                        graphType = null;
                                }

                                if (parent === null)
                                    rootElements.push(graphItem);
                            }
                            else if (htmlReader.readMode == Markup.HTMLReaderModes.Tag) {
                                if (mode == 1) mode = 2; // (begin creating on this tag [i.e. after the root tag, since root is the "application" object itself])

                                if (!graphItem) {
                                    // ... start of a new sibling tag ...
                                    properties = {};
                                    tagStartIndex = htmlReader.textEndIndex; // (the text end index is the start of the tag)
                                    if (mode == 2)
                                        storeRunningText(parent);
                                } else if (mode == 2) {
                                    // (note: each function call deals with a single nested level, and if a tag is not closed upon reading another, 'processTag' is called again)
                                    immediateChildTemplates = processTags(graphItem); // ('graphItem' was just created for the last tag read, but the end tag is still yet to be read)
                                    if (htmlReader.tagName != graphItem.htmlTag)
                                        throw Exception.from("The closing tag '</" + htmlReader.tagName + ">' was unexpected for current tag '<" + graphItem.htmlTag + ">' on line " + htmlReader.getCurrentLineNumber() + ".");
                                    continue; // (need to continue on the last item read before returning)
                                }

                                if (currentTagName == "body" && !approotID)
                                    mode = 1; // (body was found, and the 'approotid' attribute was not specified, so assume body as the application's root element)
                            }
                        }
                    }
                }

                htmlReader.readNext();
            }

            return templates;
        };

        htmlReader.readNext(); // (move to the first item)
        processTags(null);

        log.write("HTML template parsing complete.").parent.endCapture();

        return { rootElements: rootElements, templates: dataTemplates };
    }

    // =======================================================================================================================
} // (end Applications)

// =======================================================================================================================
// Extend features of GraphItem for Array and JQuery.

// TODO: Consider how the following affects the new modules load order.  Perhaps there needs to be a special DreamSpace.Modules.jQuery module to properly extend this.
//(function () {
//    /** Extends an existing TypeScript class to contain other prototypes for special cases. 
//    * (note: only works once to extend non-extended classes)
//    */
//    function __extend(type: Function, proto: { constructor: Object }) {
//        function __() { this.constructor = proto.constructor; }
//        __.prototype = proto;
//        // ... copy any already defined properties..
//        var newProto = new __();
//        for (var p in type.prototype)
//            if (type.prototype.hasOwnProperty(p))
//                newProto[p] = type.prototype[p];
//        type.prototype = newProto;
//    };

//    // (allow the 'children' array to inherit some JQuery functionality [such as 'each()'])
//    if (typeof jQuery != "undefined" && jQuery.fn)
//        __extend(DreamSpace.GraphItem, jQuery.fn);

//    // (give array functionality)
//    //??for (var p in Array.prototype)
//    //    if (Array.prototype.hasOwnProperty(p))
//    //        DreamSpace.GraphItem.$Type.prototype[p] = Array.prototype[p];
//})();
